package ch.subgate.spritestudio.widgets;

import java.util.ArrayList;

import ch.subgate.spritestudio.R;
import ch.subgate.spritestudio.constants.Constants;
import ch.subgate.spritestudio.types.RGBAListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class RGBASeekBar extends View implements RGBAListener {
	
	private float mWidth;
	
	private int mR;
	
	private int mG;
	
	private int mB;
	
	private int mA;
	
	private int mRelevancy;
	
	private float mSliderPos;
	
	private Paint mShaderPaint;
	
	private Paint mPatternPaint;
	
	private LinearGradient mShader;
	
	private ArrayList<RGBAListener> mListeners;

	public RGBASeekBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RGBASeekBar);
		mRelevancy = a.getInt(R.styleable.RGBASeekBar_relevancy, 0);
		a.recycle();
		
		mWidth = 0.0f;
		
		mShaderPaint = new Paint();
		mPatternPaint = new Paint();
		
		mA = 0x000000ff;
		mR = mG = mB = 0x00000000;
		
		mListeners = new ArrayList<RGBAListener>();
	}
	
	@Override
	public void onDraw(Canvas canvas) {
		//Draw background pattern
		for (int i = 0; i * 36 < this.getMeasuredWidth(); i++) {
			for (int j = 0; j * 36 < this.getMeasuredHeight(); j++) {
				if ((i + j) % 2 == 0) {
					mPatternPaint.setColor(0xffaaaaaa);
					canvas.drawRect(
							(float)(i * 36),
							(float)(j * 36),
							(float)((i + 1) * 36),
							(float)((j + 1) * 36),
							mPatternPaint);
				}
				else {
					mPatternPaint.setColor(0xffcccccc);
					canvas.drawRect(
							(float)(i * 36),
							(float)(j * 36),
							(float)((i + 1) * 36),
							(float)((j + 1) * 36),
							mPatternPaint);
				}
			}
		}
		
		mShader = getShader();
		mShaderPaint.setShader(mShader);
		
		// Draw shader
		canvas.drawRect(
				0.0f,
				0.0f,
				(float)(this.getMeasuredWidth()),
				(float)(this.getMeasuredHeight()),
				mShaderPaint);
		
		// Draw slider
		mPatternPaint.setColor(0x99000000);
		mPatternPaint.setStrokeWidth(9.0f);
		canvas.drawLine(
				mSliderPos, 0.0f,
				mSliderPos, (float)this.getMeasuredHeight(),
				mPatternPaint);
		mPatternPaint.setColor(0xffffffff);
		mPatternPaint.setStrokeWidth(3.0f);
		canvas.drawLine(
				mSliderPos, 0.0f,
				mSliderPos, (float)this.getMeasuredHeight(),
				mPatternPaint);
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		
		mWidth = (float)w;
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float eventx;
		float newSliderPos;
		float clampDist = mWidth / 255.0f * Constants.RGBA_CLAMP_INTERVAL;
		
		if (event.getX() > mWidth) {
			eventx = mWidth;
		}
		else if (event.getX() < 0.0f) {
			eventx = 0.0f;
		}
		else {
			eventx = event.getX();
		}
		
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
		case MotionEvent.ACTION_MOVE:
			newSliderPos = clampDist * (float)Math.round(eventx / clampDist);
			if (newSliderPos != mSliderPos) {
				mSliderPos = newSliderPos;
				invalidate();
			}
			break;
		case MotionEvent.ACTION_UP:
			newSliderPos = clampDist * (float)Math.round(eventx / clampDist);
			if (newSliderPos != mSliderPos) {
				mSliderPos = newSliderPos;
				invalidate();
			}
			this.setValue((int)(Math.round(mSliderPos / mWidth * 255.0f)), mRelevancy);
			break;
		}
		return true;
	}
	
	public LinearGradient getShader() {
		int startColor, endColor;
		switch (mRelevancy) {
		case Constants.COLOR_R_RELEVANT:
			startColor =
					0xff000000
					| (mG << Constants.COLOR_G_OFFSET)
					| (mB << Constants.COLOR_B_OFFSET);
			endColor = startColor | (0x00ff0000);
			break;
		case Constants.COLOR_G_RELEVANT:
			startColor =
					0xff000000
					| (mR << Constants.COLOR_R_OFFSET)
					| (mB << Constants.COLOR_B_OFFSET);
			endColor = startColor | (0x0000ff00);
			break;
		case Constants.COLOR_B_RELEVANT:
			startColor =
					0xff000000
					| (mR << Constants.COLOR_R_OFFSET)
					| (mG << Constants.COLOR_G_OFFSET);
			endColor = startColor | (0x000000ff);
			break;
		case Constants.COLOR_A_RELEVANT:
			startColor =
					(mR << Constants.COLOR_R_OFFSET)
					| (mG << Constants.COLOR_G_OFFSET)
					| (mB << Constants.COLOR_B_OFFSET);
			endColor = startColor | (0xff000000);
			break;
		default:
			startColor = 0;
			endColor = 0;
		}
		
		return new LinearGradient(
				0.0f,
				0.0f,
				mWidth,
				0.0f,
				startColor,
				endColor,
				Shader.TileMode.CLAMP);
	}
	
	public void addRGBAListener(RGBAListener listener) {
		if (listener != null) {
			mListeners.add(listener);
		}
	}
	
	public void setValue(int value, int relevancy) {
		switch (relevancy) {
		case Constants.COLOR_A_RELEVANT:
			mA = value;
			break;
		case Constants.COLOR_R_RELEVANT:
			mR = value;
			break;
		case Constants.COLOR_G_RELEVANT:
			mG = value;
			break;
		case Constants.COLOR_B_RELEVANT:
			mB = value;
			break;
		}
		
		for (RGBAListener listener : mListeners) {
			listener.onRGBAValueChanged(value, relevancy	);
		}
	}
	
	public void setAlpha(int alpha) {
		mA = alpha & 0x000000ff;
		
		if (mRelevancy == Constants.COLOR_A_RELEVANT) {
			for (RGBAListener listener : mListeners) {
				listener.onRGBAValueChanged(mA, mRelevancy);
			}
		}
		else {
			invalidate();
		}
	}
	
	public int getAlpha() {
		return mA;
	}
	
	public void setRed(int red) {
		mR = red & 0x000000ff;
		
		if (mRelevancy == Constants.COLOR_R_RELEVANT) {
			for (RGBAListener listener : mListeners) {
				listener.onRGBAValueChanged(mR, mRelevancy);
			}
		}
		else {
			invalidate();
		}
	}
	
	public int getRed() {
		return mR;
	}
	
	public void setGreen(int green) {
		mG = green & 0x000000ff;
		
		if (mRelevancy == Constants.COLOR_G_RELEVANT) {
			for (RGBAListener listener : mListeners) {
				listener.onRGBAValueChanged(mG, mRelevancy);
			}
		}
		else {
			invalidate();
		}
	}
	
	public int getGreen() {
		return mG;
	}
	
	public void setBlue(int blue) {
		mB = blue & 0x000000ff;
		
		if (mRelevancy == Constants.COLOR_B_RELEVANT) {
			for (RGBAListener listener : mListeners) {
				listener.onRGBAValueChanged(mB, mRelevancy);
			}
		}
		else {
			invalidate();
		}
	}
	
	public int getBlue() {
		return mB;
	}
	
	public void setRelevancy(int relevancy) {
		mRelevancy = relevancy;
		
		invalidate();
	}
	
	public int getRelevancy() {
		return mRelevancy;
	}
	
	public void init() {
		int value = 0;
		switch (mRelevancy) {
		case Constants.COLOR_A_RELEVANT:
			value = mA;
			break;
		case Constants.COLOR_R_RELEVANT:
			value = mR;
			break;
		case Constants.COLOR_G_RELEVANT:
			value = mG;
			break;
		case Constants.COLOR_B_RELEVANT:
			value = mB;
			break;
		}
		
		for (RGBAListener listener : mListeners) {
			listener.onRGBAValueChanged(value, mRelevancy);
		}
	}

	@Override
	public void onRGBAValueChanged(int value, int relevancy) {
		switch(relevancy) {
		case Constants.COLOR_A_RELEVANT:
			this.setAlpha(value);
			break;
		case Constants.COLOR_R_RELEVANT:
			this.setRed(value);
			break;
		case Constants.COLOR_G_RELEVANT:
			this.setGreen(value);
			break;
		case Constants.COLOR_B_RELEVANT:
			this.setBlue(value);
			break;
		}
	}

}
